import os
import pickle
import time

import numpy as np
import pandas as pd
import tensorflow as tf
from tensorflow import keras

from src.utils.config import logger
from src.utils.db_processor import mysql_prediction_processor
from src.laptop.model import laptop_model_config
from src.laptop.predict import laptop_predict_config
from src.utils.model_utils import load_history_avg_price
from src.laptop.preprocessor.model_data_preprocessor import process_brand_discont_mapping
from src.laptop.preprocessor.settle_data_loader import load_model_product_config_item
from src.utils.model_utils import custom_mean_absolute_percentage
from tensorflow.python.keras.backend import set_session
session_config = tf.ConfigProto(allow_soft_placement=True)
sess = tf.Session(config=session_config)
global graph
graph = tf.get_default_graph()


class LaptopPredictor:
    """
    笔记本价格预测器
    """
    def __init__(self):
        # 模型
        self.model = None
        # 特征变量One-Hot编码处理器
        self.ohe_product = None
        self.ohe_level = None
        self.ohe_attr = None
        self.ohe_period = None
        # 价格标准化
        self.scaler = None

        self.load_models()
        self.model_product_config = load_model_product_config_item()

    # def load_models(self):
    #     """
    #     读取模型与预处理器
    #     :return:
    #     """
    #     logger.info('loading models...')
    #     model_file = os.path.join(laptop_predict_config.MODEL_DIR, laptop_model_config.MODEL_FILE_NAME)
    #     with graph.as_default():
    #         set_session(sess)
    #         self.model = keras.models.load_model(model_file)
    #
    #     ohe_product_file = 'ohe_product' + laptop_model_config.PREPROCESSOR_SUFFIX_NAME
    #     with open(os.path.join(laptop_predict_config.MODEL_DIR, ohe_product_file), 'rb') as f:
    #         self.ohe_product = pickle.load(f)
    #
    #     ohe_level_file = 'ohe_level' + laptop_model_config.PREPROCESSOR_SUFFIX_NAME
    #     with open(os.path.join(laptop_predict_config.MODEL_DIR, ohe_level_file), 'rb') as f:
    #         self.ohe_level = pickle.load(f)
    #
    #     ohe_attr_file = 'ohe_attr' + laptop_model_config.PREPROCESSOR_SUFFIX_NAME
    #     with open(os.path.join(laptop_predict_config.MODEL_DIR, ohe_attr_file), 'rb') as f:
    #         self.ohe_attr = pickle.load(f)
    #
    #     ohe_period_file = 'ohe_period' + laptop_model_config.PREPROCESSOR_SUFFIX_NAME
    #     with open(os.path.join(laptop_predict_config.MODEL_DIR, ohe_period_file), 'rb') as f:
    #         self.ohe_period = pickle.load(f)
    #
    #     scaler_file = 'scaler' + laptop_model_config.PREPROCESSOR_SUFFIX_NAME
    #     with open(os.path.join(laptop_predict_config.MODEL_DIR, scaler_file), 'rb') as f:
    #         self.scaler = pickle.load(f)
    #
    #     self.__load_attr_default_price()
    #
    #     logger.info('loading models done')

    def load_models(self):
        """
        读取模型和预处理器
        :return:
        """
        logger.info('loading models...')

        model_file = os.path.join(laptop_model_config.MODEL_DIR, laptop_model_config.MODEL_FILE_NAME)
        with graph.as_default():
            set_session(sess)
            self.model = keras.models.load_model(model_file, custom_objects={
                'custom_mean_absolute_percentage': custom_mean_absolute_percentage})

        ohe_file = os.path.join(laptop_model_config.MODEL_DIR, laptop_model_config.MOBILE_OHE_NAME)
        with open(ohe_file, 'rb') as f:
            self.ohe = pickle.load(f)


        product_ohe_file = os.path.join(laptop_model_config.MODEL_DIR, laptop_model_config.MOBILE_PRODUCT_OHE_NAME)
        with open(product_ohe_file, 'rb') as f:
            self.product_ohe = pickle.load(f)

        standard_file = os.path.join(laptop_model_config.MODEL_DIR, laptop_model_config.LAPTOP_STANDARD)
        with open(standard_file, 'rb') as f:
            self.standard = pickle.load(f)

        brand_fe_file = os.path.join(laptop_model_config.MODEL_DIR, laptop_model_config.BRAND_FE)
        with open(brand_fe_file, 'rb') as f:
            self.brand_fe = pickle.load(f)

        # self.brand_fe.set_index('product_brand_id',inplace=True)

        product_fe_file = os.path.join(laptop_model_config.MODEL_DIR, laptop_model_config.PRODUCT_FE)
        with open(product_fe_file, 'rb') as f:
            self.product_fe = pickle.load(f)
        # self.product_fe.set_index('product_id', inplace=True)

        median_dict_file = os.path.join(laptop_model_config.MODEL_DIR, laptop_model_config.median_dict)
        with open(median_dict_file, 'rb') as f:
            self.median_dict = pickle.load(f)

        self.median_dict_temp = {}
        self.median_dict_temp['brand_price_skew'] = self.median_dict.get('brand_price_skew')
        self.median_dict_temp['brand_price_kurt'] = self.median_dict.get('brand_price_kurt')
        logger.info('loading models done')

        self.__load_attr_default_price()
        logger.info('loading models done')

    def __load_attr_default_price(self):
        """
        读取没有属性配置信息笔记本电脑的默认价格
        :return:
        """
        logger.info('loading default price...')
        sql = "SELECT product_level_name, actual_price as default_price FROM laptop_attr_default_price"
        self.attr_default_price = mysql_prediction_processor.load_sql(sql)

    def predict_price(self, predict_data):
        """
        预测价格
        :param predict_data: 数据，经过model_data预处理格式
        :return:
        """
        predict_data[laptop_model_config.CLASSIFICATION_FEATURES] = predict_data[
            laptop_model_config.CLASSIFICATION_FEATURES].astype(str)
        predict_data[laptop_model_config.CONTINUOUS_FEATURES] = predict_data[
            laptop_model_config.CONTINUOUS_FEATURES].astype(float)

        logger.info('predict price')
        t1 = time.time()

        test_x_inputs = self.ohe.transform(predict_data[laptop_model_config.CLASSIFICATION_FEATURES])
        x_ava_continuous = self.standard.transform(predict_data[laptop_model_config.CONTINUOUS_FEATURES])

        with graph.as_default():
            set_session(sess)
            predict_scaled = self.model.predict([test_x_inputs, x_ava_continuous])
        predict_data['price'] = np.round(predict_scaled.flatten(-1)).astype(int)

        t2 = time.time()
        logger.info('predict price use time @{}'.format(t2 - t1))

        # 处理和均价的加权
        # predict_data['key'] = laptop_model_config.LAPTOP_HISTORY_AVG_PRICE_PREFIX + \
        #                       predict_data['sku_id'].astype(str) + '_' + predict_data['level_id'].astype(str)
        # predict_data['history_avg_price'] = [load_history_avg_price(k) for k in predict_data['key']]
        # predict_data['history_avg_price'] = predict_data['history_avg_price'].where(
        #     predict_data['history_avg_price'].notnull(), predict_data['price'])

        # tmp=predict_data[['sku_id','level_id','price','history_avg_price']]
        # logger.info('未加权输出价格：{}'.format(tmp))

        # predict_data['price'] = predict_data['price'] * laptop_predict_config.MODEL_PRICE_WEIGHT + \
        #                         predict_data['history_avg_price'] * laptop_predict_config.HISTORY_AVG_PRICE_WEIGHT

        #判断是否是is_new_product
        predict_data['is_new_product'] = self.product_ohe.transform(
            predict_data[laptop_model_config.PRODUCT_FEATURES]).sum(axis=1).astype(int)
        predict_data['is_new_product'] = predict_data['is_new_product'].replace({0: 1, 1: 0})

        # predict_data['is_new_product'] = x_predict_product.sum(axis=1).astype(int)
        # predict_data['is_new_product'] = predict_data['is_new_product'].replace({0: 1, 1: 0})
        #按照level打折
        # predict_data = pd.merge(predict_data, laptop_model_config.NEW_LEVEL_COEF, on='product_level_name', how='left')
        # predict_data['coef'].where(~predict_data['coef'].isnull(), 1, inplace=True)
        # predict_data['price'] = predict_data['price'] * predict_data['coef']


        # 对无属性信息的产品使用默认价格
        if self.attr_default_price is not None:
            is_no_attr = (predict_data[laptop_model_config.NO_ATTR_CHECK_LIST].isin(
                ['unknown', 'APPLE_HDD', 'MS_HDD'])).all(axis=1)
            predict_data = pd.merge(predict_data, self.attr_default_price, on='product_level_name', how='left')
            predict_data['default_price'].fillna(5, inplace=True)
            predict_data.loc[is_no_attr, 'price'] = predict_data.loc[is_no_attr, 'default_price']
            predict_data.loc[is_no_attr, 'is_new_product'] = 0

        # 处理其他属性都有，无机械硬盘无ssd的情况
        all_have_but_hd_attr = ((~predict_data[laptop_model_config.ALL_HAVE_CHECK_LIST].isin(
            ['unknown'])).all(axis=1)) & ((predict_data[laptop_model_config.HD_CHECK_LIST].isin(
            ['不含固态硬盘', '不含机械硬盘','APPLE_HDD','MS_HDD'])).all(axis=1))
        predict_data.loc[all_have_but_hd_attr, 'price'] = \
            predict_data.loc[all_have_but_hd_attr, 'price'] * 0.2

        other_cpu = (predict_data['CPU'].isin(['其它处理器','其他处理器','非酷睿 i 系列']))
        other_gpu = (predict_data['GPU'].isin(['其它显卡','其他显卡']))
        other_product_name = (predict_data['product_name'].isin(['可开机笔记本']))

        predict_data.loc[other_cpu, 'price'] = predict_data.loc[other_cpu, 'price'] * 0.6
        predict_data.loc[other_gpu, 'price'] = predict_data.loc[other_gpu, 'price'] * 0.6
        predict_data.loc[other_product_name, 'price'] = predict_data.loc[other_product_name, 'price'] * 0.6


        # 处理最小值
        predict_data['price'] = predict_data['price'].where(predict_data['price'] >= 5, 5)
        #predict_data=process_brand_discont_mapping(predict_data)
        predict_data['price'] = predict_data['price'].astype(np.int)
        predict_data['category'] = 5

        # predict_data['price'] = predict_data['price'] * 0.99
        return predict_data
